home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / kernel / net / sun4.md / netIEXmit.c < prev    next >
C/C++ Source or Header  |  1992-12-18  |  18KB  |  607 lines

  1. /* 
  2.  * netIEXmit.c --
  3.  *
  4.  *    Routines to transmit packets on the Intel interface.
  5.  *
  6.  * Copyright 1985, 1988 Regents of the University of California
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  *
  15.  */
  16.  
  17. #ifndef lint
  18. static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/net/sun4.md/netIEXmit.c,v 9.8 92/04/14 16:57:50 jhh Exp $ SPRITE (Berkeley)";
  19. #endif
  20.  
  21. #include <sprite.h>
  22. #include <netIEInt.h>
  23. #include <sys.h>
  24. #include <list.h>
  25. #include <vmMach.h>
  26. #include <sync.h>
  27.  
  28. /*
  29.  * Extra bytes for short packets.
  30.  */
  31. char    *netIEXmitFiller;
  32.  
  33.  
  34.  
  35. /*
  36.  *----------------------------------------------------------------------
  37.  *
  38.  * OutputPacket --
  39.  *
  40.  *    Assemble and output the packet in the given scatter/gather element.
  41.  *    The ethernet header contains the address of the destination host
  42.  *    and the higher level protocol type already.
  43.  *
  44.  * Results:
  45.  *    None.
  46.  *
  47.  * Side effects:
  48.  *    Transmit command list is modified to contain the packet.
  49.  *
  50.  *----------------------------------------------------------------------
  51.  */
  52.  
  53. static void
  54. OutputPacket(etherHdrPtr, scatterGatherPtr, scatterGatherLength, statePtr)
  55.     Net_EtherHdr                        *etherHdrPtr;
  56.     register    Net_ScatterGather       *scatterGatherPtr;
  57.     int                    scatterGatherLength;
  58.     NetIEState                *statePtr;
  59. {
  60.     register    volatile NetIETransmitBufDesc    *xmitBufDescPtr;
  61.     register    volatile NetIETransmitCB       *xmitCBPtr;
  62.     register int            bufCount;
  63.     int                    totalLength;
  64.     register int            length;
  65.     register Address            bufAddr;
  66. #define VECTOR_LENGTH    20
  67.     int                    borrowedBytes[VECTOR_LENGTH];
  68.     int                    *borrowedBytesPtr;
  69.     char                *tmpBuffer;
  70.     int                    tmpBufSize;
  71. #if defined(sun3) || defined(sun4) 
  72.     Net_ScatterGather            newScatGathArr[NET_IE_NUM_XMIT_BUFFERS];
  73. #endif
  74.  
  75.     statePtr->transmitting = TRUE;
  76.     statePtr->curScatGathPtr = scatterGatherPtr;
  77. #if defined(sun3) || defined(sun4) 
  78.     /*
  79.      * Remap the packet into network addressible memory.
  80.      */
  81.     VmMach_NetMapPacket(scatterGatherPtr, scatterGatherLength, newScatGathArr);
  82.     scatterGatherPtr = newScatGathArr;
  83. #endif
  84.  
  85.     /*
  86.      * There is already a prelinked command list.  A pointer to the list
  87.      * and the array of buffer headers is gotten here.
  88.      */
  89.  
  90.     xmitCBPtr = statePtr->xmitCBPtr;
  91.     xmitBufDescPtr = statePtr->xmitBufAddr;
  92.  
  93.     totalLength = sizeof(Net_EtherHdr);
  94.  
  95.     /*
  96.      * If vector elements are two small we borrow bytes from the next
  97.      * element.  The borrowedBytes array is used to remember this.  We
  98.      * can't side-effect the main scatter-gather vector becuase that
  99.      * screws up retransmissions.
  100.      */
  101.     borrowedBytesPtr = borrowedBytes;
  102.     *borrowedBytesPtr = 0;
  103.     tmpBuffer = statePtr->netIEXmitTempBuffer;
  104.     tmpBufSize = XMIT_TEMP_BUFSIZE;
  105.     /*
  106.      * Put all of the pieces of the packet into the linked list of xmit
  107.      * buffers.
  108.      */
  109.     for (bufCount = 0 ; bufCount < scatterGatherLength ;
  110.      bufCount++, scatterGatherPtr++, borrowedBytesPtr++) {
  111.  
  112.     /*
  113.      * If is an empty buffer then skip it.  Length might even be negative
  114.      * if we have borrowed bytes from it to pad out to NET_IE_MIN_DMA_SIZE.
  115.      */
  116.     borrowedBytesPtr[1] = 0;
  117.     length = scatterGatherPtr->length - *borrowedBytesPtr;
  118.     if (length <= 0) {
  119.         continue;
  120.     }
  121.     bufAddr = scatterGatherPtr->bufAddr + *borrowedBytesPtr;
  122.     /*
  123.      * If the buffer is too small then it needs to be made bigger
  124.      * or the DMA hardware will overrun.  Also, check for buffers
  125.      * that start at odd addresses.  If one does, then it needs
  126.      * to be copied to another buffer with an even address.
  127.      * NB: There is only one temporary buffer.  Bad things will happen
  128.      * if more than one message uses this temporary buffer at once.
  129.      */
  130.     if ((length < NET_IE_MIN_DMA_SIZE) || ((int)bufAddr & 0x1)) {
  131.  
  132.         if (length > tmpBufSize) {
  133.         statePtr->transmitting = FALSE;
  134.         ENABLE_INTR();
  135.  
  136.         panic("IE OutputPacket: Odd addressed buffer too large.");
  137.         return;
  138.         }
  139.         bcopy(bufAddr, tmpBuffer, length);
  140.         if (length < NET_IE_MIN_DMA_SIZE) {
  141.         /*
  142.          * This element of the scatter/gather vector is too small;
  143.          * the controller DMA has to copy a minimum number of bytes.
  144.          * We take some bytes from the next non-zero sized element(s)
  145.          * to pad this one out.
  146.          */
  147.         register int numBorrowedBytes;
  148.         register int numAvailableBytes;
  149.         while (bufCount < scatterGatherLength - 1) {
  150.             numBorrowedBytes = NET_IE_MIN_DMA_SIZE - length;
  151.             numAvailableBytes = scatterGatherPtr[1].length -
  152.                     borrowedBytesPtr[1];
  153.             if (numBorrowedBytes > numAvailableBytes) {
  154.             numBorrowedBytes = numAvailableBytes;
  155.             }
  156.             if (numBorrowedBytes > 0) {
  157.             bcopy(scatterGatherPtr[1].bufAddr,
  158.                  &tmpBuffer[length], numBorrowedBytes);
  159.             borrowedBytesPtr[1] = numBorrowedBytes;
  160.             length += numBorrowedBytes;
  161.             }
  162.             if (length == NET_IE_MIN_DMA_SIZE) {
  163.             break;
  164.             } else {
  165.             bufCount++;
  166.             scatterGatherPtr++;
  167.             borrowedBytesPtr++;
  168.             borrowedBytesPtr[1] = 0;
  169.             }
  170.         }
  171.         length = NET_IE_MIN_DMA_SIZE;
  172.         }
  173.  
  174.         NET_IE_ADDR_FROM_SUN_ADDR(
  175.          (int) (tmpBuffer), (int) (xmitBufDescPtr->bufAddr));
  176.         /*
  177.          * Set up tmpBuffer for the next short segment.
  178.          */
  179.         tmpBuffer += length;
  180.         tmpBufSize -= length;
  181.         if ((int)tmpBuffer & 0x1) {
  182.         tmpBuffer++;
  183.         tmpBufSize--;
  184.         }
  185.     } else {
  186.         NET_IE_ADDR_FROM_SUN_ADDR(
  187.          (int) (bufAddr), (int) (xmitBufDescPtr->bufAddr));
  188.     }
  189.     NetBfShortSet(xmitBufDescPtr->bits, Eof, 0);
  190.     NetBfShortSet(xmitBufDescPtr->bits, CountLow, length & 0xFF);
  191.     NetBfShortSet(xmitBufDescPtr->bits, CountHigh, length >> 8);
  192.  
  193.     totalLength += length;
  194.     xmitBufDescPtr = (volatile NetIETransmitBufDesc *)
  195.         ((int) xmitBufDescPtr + NET_IE_CHUNK_SIZE);
  196.     }
  197.  
  198.     /*
  199.      * If the packet was too short, then hang some extra storage off of the
  200.      * end of it.
  201.      */
  202.  
  203.     if (totalLength < NET_ETHER_MIN_BYTES) {
  204.         NET_IE_ADDR_FROM_SUN_ADDR((int) netIEXmitFiller, 
  205.                         (int) xmitBufDescPtr->bufAddr); 
  206.     length = NET_ETHER_MIN_BYTES - totalLength;
  207.     if (length < MIN_XMIT_BUFFER_SIZE) {
  208.         length = MIN_XMIT_BUFFER_SIZE;
  209.     }
  210.     NetBfShortSet(xmitBufDescPtr->bits, CountLow, length & 0xFF);
  211.     NetBfShortSet(xmitBufDescPtr->bits, CountHigh, length >> 8);
  212.     } else {
  213.     xmitBufDescPtr = (volatile NetIETransmitBufDesc *)
  214.         ((int) xmitBufDescPtr - NET_IE_CHUNK_SIZE);
  215.     }
  216.  
  217.     /*
  218.      * Finish off the packet.
  219.      */
  220.  
  221.     NetBfShortSet(xmitBufDescPtr->bits, Eof, 1);
  222.     xmitCBPtr->destEtherAddr = etherHdrPtr->destination;
  223.     xmitCBPtr->type = etherHdrPtr->type;
  224.  
  225.     /*
  226.      * Append the command onto the command queue.
  227.      */
  228.  
  229.     *(short *) xmitCBPtr = 0;      /* Clear the status bits. */
  230.     NetBfWordSet(xmitCBPtr->bits, EndOfList, 1);
  231.     NetBfWordSet(xmitCBPtr->bits, Interrupt, 1);
  232.  
  233.  
  234.     /*
  235.      * Make sure that the last command was accepted and then
  236.      * start the command unit.
  237.      */
  238.  
  239.     NET_IE_CHECK_SCB_CMD_ACCEPT(statePtr->scbPtr);
  240.     NetBfShortSet(statePtr->scbPtr->cmdWord, CmdUnitCmd, NET_IE_CUC_START);
  241.     NET_IE_CHANNEL_ATTENTION(statePtr);
  242. }
  243.  
  244.  
  245. /*
  246.  *----------------------------------------------------------------------
  247.  *
  248.  * NetIEXmitInit --
  249.  *
  250.  *    Initialize the transmission queue structures.  This includes setting
  251.  *    up a template transmission command block and then if any packets are
  252.  *    ready starting to transmit.
  253.  *
  254.  * Results:
  255.  *    None.
  256.  *
  257.  * Side effects:
  258.  *    The transmission command block is initialized.
  259.  *
  260.  *----------------------------------------------------------------------
  261.  */
  262.  
  263. void
  264. NetIEXmitInit(statePtr)
  265.     NetIEState        *statePtr;
  266. {
  267.     register volatile NetIETransmitCB        *xmitCBPtr;
  268.     register volatile NetIETransmitBufDesc  *xmitBufDescPtr;
  269.     volatile NetIETransmitBufDesc        *newXmitBufDescPtr;
  270.     volatile NetXmitElement                *xmitElementPtr;
  271.     int         i;
  272.  
  273.     /*
  274.      * Initialize the transmit command header.
  275.      */
  276.  
  277.     xmitCBPtr = (NetIETransmitCB *) statePtr->cmdBlockPtr;
  278.     statePtr->xmitCBPtr = xmitCBPtr;
  279.     NetBfWordSet(xmitCBPtr->bits, CmdNumber, NET_IE_TRANSMIT);
  280.     NetBfWordSet(xmitCBPtr->bits, Suspend, 0);
  281.  
  282.     /*
  283.      * Now link in all of the buffer headers.
  284.      */
  285.  
  286.     xmitBufDescPtr = (volatile NetIETransmitBufDesc *) NIL;
  287.     for (i = 0; i < NET_IE_NUM_XMIT_BUFFERS; i++) {
  288.     newXmitBufDescPtr = (volatile NetIETransmitBufDesc *) 
  289.                 NetIEMemAlloc(statePtr);
  290.     if (newXmitBufDescPtr == (volatile NetIETransmitBufDesc *) NIL) {
  291.         panic( "Intel: No memory for the xmit buffers.\n");
  292.     }
  293.  
  294.     if (i == 0) {
  295.         statePtr->xmitBufAddr = newXmitBufDescPtr;
  296.         xmitCBPtr->bufDescOffset = 
  297.             NetIEOffsetFromSUNAddr((int) newXmitBufDescPtr,
  298.                 statePtr);
  299.     } else {
  300.         xmitBufDescPtr->nextTBD = 
  301.             NetIEOffsetFromSUNAddr((int) newXmitBufDescPtr,
  302.                 statePtr);
  303.     }
  304.  
  305.     xmitBufDescPtr = newXmitBufDescPtr;
  306.     }
  307.  
  308.     /*
  309.      * If there are packets on the queue then go ahead and send 
  310.      * the first one.
  311.      */
  312.  
  313.     if (!List_IsEmpty(statePtr->xmitList)) {
  314.     xmitElementPtr = (NetXmitElement *) List_First(statePtr->xmitList);
  315.     OutputPacket(xmitElementPtr->etherHdrPtr,
  316.              xmitElementPtr->scatterGatherPtr,
  317.              xmitElementPtr->scatterGatherLength, statePtr);
  318.     List_Move((List_Links *) xmitElementPtr, 
  319.           LIST_ATREAR(statePtr->xmitFreeList));
  320.     } else {
  321.     statePtr->transmitting = FALSE;
  322.     statePtr->curScatGathPtr = (Net_ScatterGather *) NIL;
  323.     }
  324.     return;
  325. }
  326.  
  327.  
  328. /*
  329.  *----------------------------------------------------------------------
  330.  *
  331.  * NetIEXmitDone --
  332.  *
  333.  *    This routine will process a completed transmit command.  It will
  334.  *    remove the command from the front of the transmit queue, 
  335.  *    and wakeup any waiting process.
  336.  *
  337.  * Results:
  338.  *    None.
  339.  *
  340.  * Side effects:
  341.  *    None.
  342.  *
  343.  *----------------------------------------------------------------------
  344.  */
  345.  
  346. void
  347. NetIEXmitDone(statePtr)
  348.     NetIEState    *statePtr;
  349. {
  350.     register    volatile NetXmitElement     *xmitElementPtr;
  351.     register    volatile NetIETransmitCB    *cmdPtr;
  352.     Net_ScatterGather    *curScatGathPtr;
  353.  
  354.     /*
  355.      * If there is nothing that is currently being sent then something is
  356.      * wrong.
  357.      */
  358.     if (statePtr->curScatGathPtr == (Net_ScatterGather *) NIL) {
  359. #ifndef sun4
  360.     /*
  361.      * Need to fix this for the sun4.
  362.      */
  363.     printf( "NetIEXmitDone: No current packet\n.");
  364. #endif
  365.     return;
  366.     }
  367.     curScatGathPtr = statePtr->curScatGathPtr;
  368.  
  369.     statePtr->stats.packetsSent++;
  370.  
  371.     /*
  372.      * Mark the packet as done.
  373.      */
  374.     curScatGathPtr->done = TRUE;
  375.     if (curScatGathPtr->mutexPtr != (Sync_Semaphore *) NIL) {
  376.     NetOutputWakeup(curScatGathPtr->mutexPtr);
  377.     }
  378.  
  379.     /*
  380.      * Record statistics about the packet.
  381.      */
  382.     cmdPtr = statePtr->xmitCBPtr;
  383.     if (NetBfWordTest(cmdPtr->bits, TooManyCollisions, 1)) {
  384.     statePtr->stats.xmitCollisionDrop++;
  385.     statePtr->stats.collisions += 16;
  386.     } else {
  387.     statePtr->stats.collisions += NetBfWordGet(cmdPtr->bits, NumCollisions);
  388.     }
  389.     if (NetBfWordTest(cmdPtr->bits, CmdOK, 0)) {
  390.     statePtr->stats.xmitPacketsDropped++;
  391.     }
  392.  
  393.     /*
  394.      * If there are more packets to send then send the first one on
  395.      * the queue.  Otherwise there is nothing being transmitted.
  396.      */
  397.     if (!List_IsEmpty(statePtr->xmitList)) {
  398.     xmitElementPtr = (NetXmitElement *) List_First(statePtr->xmitList);
  399.     OutputPacket(xmitElementPtr->etherHdrPtr,
  400.              xmitElementPtr->scatterGatherPtr,
  401.              xmitElementPtr->scatterGatherLength, statePtr);
  402.     List_Move((List_Links *) xmitElementPtr, 
  403.           LIST_ATREAR(statePtr->xmitFreeList));
  404.     } else {
  405.     statePtr->transmitting = FALSE;
  406.     statePtr->curScatGathPtr = (Net_ScatterGather *) NIL;
  407.     }
  408.     return;
  409. }
  410.  
  411.  
  412. /*
  413.  *----------------------------------------------------------------------
  414.  *
  415.  * NetIEOutput --
  416.  *
  417.  *    Output a packet.  The procedure is to either put the packet onto the 
  418.  *    queue of outgoing packets if packets are already being sent, or 
  419.  *    otherwise to send the packet directly.  The elements of the scatter 
  420.  *    array which come into this routine must satisfy the following two 
  421.  *    properties:
  422.  *
  423.  *    1) No buffer must be below the size MIN_XMIT_BUFFER_SIZE.  If one is
  424.  *       then a warning will be printed and there is a good chance that
  425.  *       the packet will not make it.
  426.  *    2) No buffer can start on an odd boundary.  It appears that the Intel
  427.  *       chip drops the low order bit of the address.  Thus if a buffer
  428.  *       begins on an odd boundary the actual buffer sent will have one
  429.  *       extra byte at the front.  If a buffer does begin on an odd 
  430.  *       boundary then a warning message is printed and the packet is
  431.  *       sent anyway.
  432.  *
  433.  *    In theory the statusPtr argument should be filled in by NetIEXmitDone
  434.  *    when the command completes.  We fill it in here because a 
  435.  *    mechansism doesn't exist for getting the pointer to NetIEXmitDone,
  436.  *    and because it doesn't appear that NetIEXmitDone would ever
  437.  *    return anything other than SUCCESS.
  438.  *
  439.  * Results:
  440.  *    None.
  441.  *
  442.  * Side effects:
  443.  *    Queue of packets modified.
  444.  *
  445.  *----------------------------------------------------------------------
  446.  */
  447.  
  448. ReturnStatus
  449. NetIEOutput(interPtr, hdrPtr, scatterGatherPtr, scatterGatherLength, rpc,
  450.         statusPtr)
  451.     Net_Interface            *interPtr;
  452.     Address                hdrPtr;
  453.     register    Net_ScatterGather    *scatterGatherPtr;
  454.     int                    scatterGatherLength;
  455.     Boolean                rpc;        /* Is this an rpc? */
  456.     ReturnStatus            *statusPtr;  /* Return status. */
  457. {
  458.     register volatile NetXmitElement    *xmitPtr;
  459.     NetIEState                *statePtr;
  460.     int                    i;
  461.     Net_ScatterGather            *gathPtr;
  462.     Net_EtherHdr            *etherHdrPtr = (Net_EtherHdr *) hdrPtr;
  463.  
  464.     statePtr = (NetIEState *) interPtr->interfaceData;
  465.     DISABLE_INTR();
  466.  
  467.     statePtr->stats.packetsOutput++;
  468.  
  469.     /*
  470.      * Verify that the scatter gather array is not too large.  There is a fixed
  471.      * upper bound because the list of transmit buffers is preallocated.
  472.      */
  473.  
  474.     if (scatterGatherLength >= NET_IE_NUM_XMIT_BUFFERS) {
  475.     scatterGatherPtr->done = TRUE;
  476.  
  477.     printf("Intel: Packet in too many pieces\n");
  478.     ENABLE_INTR();
  479.     return FAILURE;
  480.     }
  481.     statePtr->stats.bytesSent += sizeof(Net_EtherHdr);
  482.     for (i = scatterGatherLength, gathPtr = scatterGatherPtr; 
  483.      i > 0; 
  484.      i--, gathPtr++) { 
  485.     statePtr->stats.bytesSent += gathPtr->length; 
  486.     } 
  487.  
  488.     /*
  489.      * See if the packet is for us.  In this case just copy in the packet
  490.      * and call the higher level routine.
  491.      */
  492.  
  493.     if (!Net_EtherAddrCmp(statePtr->etherAddress, etherHdrPtr->destination)) {
  494.     int i, length;
  495.  
  496.         length = sizeof(Net_EtherHdr);
  497.         for (i = 0; i < scatterGatherLength; i++) {
  498.             length += scatterGatherPtr[i].length;
  499.         }
  500.  
  501.         if (length <= NET_ETHER_MAX_BYTES) {
  502.         register Address bufPtr;
  503.  
  504.         etherHdrPtr->source = statePtr->etherAddress;
  505.  
  506.         bufPtr = (Address)statePtr->loopBackBuffer;
  507.         bcopy((Address)etherHdrPtr, bufPtr, sizeof(Net_EtherHdr));
  508.         bufPtr += sizeof(Net_EtherHdr);
  509.             Net_GatherCopy(scatterGatherPtr, scatterGatherLength, bufPtr);
  510.  
  511.         Net_Input(interPtr, (Address)statePtr->loopBackBuffer, 
  512.             length);
  513.         }
  514.  
  515.         scatterGatherPtr->done = TRUE;
  516.     if (statusPtr != (ReturnStatus *) NIL) {
  517.         *statusPtr = SUCCESS;
  518.     }
  519.     ENABLE_INTR();
  520.     return SUCCESS;
  521.     }
  522.  
  523.     /*
  524.      * If no packet is being sent then go ahead and send this one.
  525.      */
  526.  
  527.     if (!statePtr->transmitting) {
  528.     OutputPacket(etherHdrPtr, scatterGatherPtr, scatterGatherLength,
  529.         statePtr);
  530.     if (statusPtr != (ReturnStatus *) NIL) {
  531.         *statusPtr = SUCCESS;
  532.     }
  533.     ENABLE_INTR();
  534.     return SUCCESS;
  535.     }
  536.  
  537.     /*
  538.      * There is a packet being sent so this packet has to be put onto the
  539.      * transmission queue.  Get an element off of the transmission free list.  
  540.      * If none available then drop the packet.
  541.      */
  542.  
  543.     if (List_IsEmpty(statePtr->xmitFreeList)) {
  544.         scatterGatherPtr->done = TRUE;
  545.     ENABLE_INTR();
  546.     return FAILURE;
  547.     }
  548.  
  549.     xmitPtr = (volatile NetXmitElement *)
  550.     List_First((List_Links *) statePtr->xmitFreeList);
  551.  
  552.     List_Remove((List_Links *) xmitPtr);
  553.  
  554.     /*
  555.      * Initialize the list element.
  556.      */
  557.  
  558.     xmitPtr->etherHdrPtr = etherHdrPtr;
  559.     xmitPtr->scatterGatherPtr = scatterGatherPtr;
  560.     xmitPtr->scatterGatherLength = scatterGatherLength;
  561.  
  562.     /* 
  563.      * Put onto the transmission queue.
  564.      */
  565.  
  566.     List_Insert((List_Links *) xmitPtr, LIST_ATREAR(statePtr->xmitList)); 
  567.  
  568.     if (statusPtr != (ReturnStatus *) NIL) {
  569.     *statusPtr = SUCCESS;
  570.     }
  571.     ENABLE_INTR();
  572.     return SUCCESS;
  573. }
  574.  
  575. /*
  576.  *----------------------------------------------------------------------
  577.  *
  578.  * NetIEXmitDrop --
  579.  *
  580.  *    This drops the current output packet by marking its scatter/gather
  581.  *    vector as DONE and notifying the process waiting for its
  582.  *    output to complete.  This is called in the beginning of the
  583.  *    Restart sequence.
  584.  *
  585.  * Results:
  586.  *    None.
  587.  *
  588.  * Side effects:
  589.  *    Resets curScatGathPtr and notifies any process waiting on output.
  590.  *
  591.  *----------------------------------------------------------------------
  592.  */
  593.  
  594. void
  595. NetIEXmitDrop(statePtr)
  596.     NetIEState        *statePtr;
  597. {
  598.     if (statePtr->curScatGathPtr != (Net_ScatterGather *) NIL) {
  599.     statePtr->curScatGathPtr->done = TRUE;
  600.     if (statePtr->curScatGathPtr->mutexPtr != (Sync_Semaphore *) NIL) {
  601.         NetOutputWakeup(statePtr->curScatGathPtr->mutexPtr);
  602.     }
  603.     statePtr->curScatGathPtr = (Net_ScatterGather *) NIL;
  604.     }
  605.     return;
  606. }
  607.